﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Quartz;
using RuoVea.ExDto;
using RuoVea.ExEnum;
using RuoVea.ExIdGen;
using RuoVea.ExUtil;
using RuoVea.ExUtil.Exceptions;
using RuoVea.ExUtil.Validate;
using RuoVea.QuartzNetUI.Entitys;
using RuoVea.QuartzNetUI.Server;
using RuoVea.QuartzNetUI.Server.Dto;
using RuoVea.QuartzNetUI.Server.Enums;
using RuoVea.QuartzNetUI.Server.Scheduler;
using SqlSugar;

namespace RuoVea.QuartzNetUI.Controllers
{
    /// <summary>
    /// QuartzNet UI API接口
    /// </summary>
    [Route("job")]
    [ApiController]
    public partial class QuartzJobController : Controller
    {
        private readonly ITaskSchedulerServer _taskSchedulerServer;
        private readonly ITaskLogService _taskLogService;
        private readonly ITaskDetailService _taskDetailService;
        private readonly IScheduler _scheduler;
        private readonly ISqlSugarClient _sqlSugarClient;

        private readonly QuartzOptions _quartzOptions;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sqlSugarClient"></param>
        /// <param name="quartzOptions"></param>
        /// <param name="taskSchedulerServer"></param>
        /// <param name="taskLogService"></param>
        /// <param name="taskDetailService"></param>
        /// <param name="schedulerFactory"></param>
        public QuartzJobController(ISqlSugarClient sqlSugarClient, IOptions<QuartzOptions> quartzOptions,
            ITaskSchedulerServer taskSchedulerServer, ITaskLogService taskLogService, ITaskDetailService taskDetailService, ISchedulerFactory schedulerFactory)
        {
            _sqlSugarClient = sqlSugarClient;
            _quartzOptions = quartzOptions.Value;
            _taskSchedulerServer = taskSchedulerServer;
            _taskLogService = taskLogService;
            _taskDetailService = taskDetailService;
            _scheduler = schedulerFactory.GetScheduler().Result;
        }

        /// <summary>
        /// 调度任务
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [HttpGet("dashboard")]
        public IActionResult Index() => View();

        #region log
        /// <summary>
        /// 任务执行日志
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        [HttpGet("log")]
        public IActionResult JobLog(long Id)
        {
            ViewBag.Id = Id;
            return View();
        }
        #endregion

        /// <summary>
        /// 设置
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        [HttpGet("setting")] public IActionResult Setting(long Id) => View(Id);

        /// <summary>
        /// 保存设置
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [HttpPost("savesetting")]
        public async Task<RestfulResult> SaveSetting(TaskSettingDto data)
        {
            var resultData = await _taskDetailService.SaveSetting(data);
            return resultData;
        }

        /// <summary>
        /// 获取设置信息
        /// </summary>
        /// <param name="data">code</param>
        /// <returns></returns>
        [HttpGet("getstingbycode")]
        public async Task<RestfulResult> GetSettingByCode(string data)
        {
            var resultData = await _taskDetailService.GetSettingByCode(data);
            return resultData;
        }

        /// <summary>
        /// 执行分析
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        [HttpGet("period")] public IActionResult Period(long Id) => View(Id);

        #region 获取执行分析数据
        /// <summary>
        /// 获取执行分析数据
        /// </summary>
        /// <param name="Id">任务主键</param>
        /// <returns></returns>
        [HttpGet("periodData/{Id:long}")]
        public dynamic GetPeriodData(long Id)
        {
            var day30 = Enumerable.Range(-2, 30).Select(it => DateTime.Now.Date.AddDays(it * -1)).ToList();
            //var time = DateTime.Now;
            //var days = (time.AddMonths(1) - time).Days;//获取1月天数
            //var dayArray = Enumerable.Range(1, days).Select(it => Convert.ToDateTime(time.ToString("yyyy-MM-" + it))).ToList();//转成时间数组
            var queryableLeft = _sqlSugarClient.Reportable(day30).ToQueryable<DateTime>();
            var queryableRight = _sqlSugarClient.Queryable<TaskLog>().Where(x => x.Deleted == ExEnum.IsDelete.N);
            var listGroupBy = _sqlSugarClient.Queryable(queryableLeft, queryableRight, JoinType.Left, (x1, x2) => x1.ColumnName.Date == x2.CreateTime.Date);

            var list = listGroupBy.GroupBy((x1, x2) => x1.ColumnName).Select((x1, x2) => new
            {
                count = SqlFunc.AggregateSum(SqlFunc.IIF((x2.Id > 0), 1, 0)),
                county = SqlFunc.AggregateSum(SqlFunc.IIF((x2.Status == ExEnum.YesOrNot.Y && x2.Id > 0), 1, 0)),
                countn = SqlFunc.AggregateSum(SqlFunc.IIF((x2.Status == ExEnum.YesOrNot.N && x2.Id > 0), 1, 0)),
                day = x1.ColumnName.Day
            }).ToList();
            var day = list.Select(x => x.day).ToArray();
            var lineMarker = new List<object> {
                new { name= "执行总数", type="line", data=list.Select(x=>x.count).ToArray() },
                new { name= "执行成功", type="line", data=list.Select(x=>x.county).ToArray() },
                new { name= "执行失败", type="line", data=list.Select(x=>x.countn).ToArray() }
            };

            return (
                day,
                lineMarker
                );
        }
        #endregion 

        #region info
        /// <summary>
        /// QuartzNet Info
        /// </summary>
        /// <returns></returns>
        [HttpGet("info")]
        public IActionResult Info()
        {
            var nameInfoValues = new List<KeyValuePair<string, string>>{
                 KeyValuePair.Create(nameof(_scheduler.SchedulerInstanceId),_scheduler.SchedulerInstanceId ),
                  KeyValuePair.Create(nameof(_scheduler.SchedulerName), _scheduler.SchedulerName)
            };
            foreach (var item in _quartzOptions)
                nameInfoValues.Add(KeyValuePair.Create(item.Key, item.Value));
            ViewBag.nameInfoValues = nameInfoValues;
            return View();
        }
        #endregion

        #region 获取job日志
        /// <summary>
        /// 获取job日志
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [HttpPost("getjoblogs")]
        public async Task<dynamic> GetJobLogs(TaskLogPageDto data)
        {
            var resultData = await _taskLogService.GetTaskLogPageListAsync(data);
            return resultData;
        }
        #endregion

        #region 查询单个计划任务
        /// <summary>
        /// 查询单个计划任务
        /// </summary>
        /// <param name="Id">任务主键</param>
        /// <returns></returns>
        [HttpGet("getjob/{Id:long}")]
        public async Task<dynamic> GetJob(long Id)
        {
            var resultData = await _taskDetailService.GetByIdAsync(Id);
            return resultData;
        }
        #endregion

        #region JobList
        /// <summary>
        /// 获取调度任务列表
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [HttpPost("JobList")]
        public async Task<dynamic> JobList(TaskDetailPageDto data)
        {
            var resultData = await _taskDetailService.GetTaskDetailPageListAsync(data);
            if (resultData.Code == CodeStatus.OK)
            {

                foreach (var item in resultData.Data.Rows)
                {
                    JobKey jobKey = new JobKey(item.Id + "", item.JobGroup);
                    var jobDetail = await _scheduler.GetJobDetail(jobKey);
                    if (jobDetail != null)
                    {
                        var triggersList = await _scheduler.GetTriggersOfJob(jobKey);
                        var triggers = triggersList.AsEnumerable().FirstOrDefault();
                        //var interval = string.Empty;
                        //if (triggers is SimpleTriggerImpl)
                        //    interval = (triggers as SimpleTriggerImpl)?.RepeatCount.ToString();
                        //else
                        //    interval = (triggers as CronTriggerImpl)?.CronExpressionString;

                        item.SchedName = _scheduler.SchedulerName;

                        item.LastException = jobDetail?.JobDataMap.GetLastException();

                        //item.RequestUrl = jobDetail.JobDataMap.GetRequestUrl();
                        //item.Headers = jobDetail.JobDataMap.GetHeaders();
                        //item.Parameters = jobDetail.JobDataMap.GetParameters();

                        //item.MailMsgType = jobDetail.JobDataMap.GetMailMsgType();
                        //item.SendMailers = jobDetail.JobDataMap.GetSendMailers();

                        item.TriggerState = await _scheduler.GetTriggerState(triggers?.Key);
                        item.PreviousFireTime = triggers?.GetPreviousFireTimeUtc()?.LocalDateTime;
                        item.NextFireTime = triggers?.GetNextFireTimeUtc()?.LocalDateTime;
                        //item.Interval = interval;

                        item.BeginTime = triggers?.StartTimeUtc.LocalDateTime;
                        item.EndTime = triggers?.EndTimeUtc?.LocalDateTime;
                        //item.Description = jobDetail.Description;
                    }
                }
            }

            return resultData;
        }
        #endregion

        #region save
        /// <summary>
        /// 新增或修改调度任务
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        /// <exception cref="ParamiterException"></exception>
        [HttpPost("jobsave")]
        public async Task<dynamic> save(TaskDetailInDto data)
        {
            RestfulResult restfulResult = new RestfulResult() { Code = CodeStatus.BadRequest };
            if (data == null) throw new ParamiterException($"未有参数");
            if (!ValidatorUtil.IsSzzmChinese(data.JobName))
            {
                restfulResult.Message = "JobName 只能有字母、数字、中文";
                return restfulResult;
            }
            if (!ValidatorUtil.IsSzzmChinese(data.JobGroup))
            {
                restfulResult.Message = "JobGroup 只能有字母、数字、中文";
                return restfulResult;
            }

            try
            {
                data.SchedName = _scheduler.SchedulerName;
                if (data.Id == 0)
                    return await Create(data);
                else
                    return await Update(data);
            }
            catch (ParamiterException ex)
            {
                restfulResult.Message = ex.Message;
            }
            catch (Exception ex)
            {
                restfulResult.Message = ex.Message;
            }
            return restfulResult;
        }
        #endregion

        #region 添加任务
        /// <summary>
        /// 添加任务
        /// </summary>
        /// <returns></returns>
        private async Task<dynamic> Create(TaskDetailInDto data)
        {
            //判断是否已经存在
            if (await _taskDetailService.AnyAsync(m => m.JobName == data.JobName))
                throw new ParamiterException($"添加 {data.JobName} 失败，该任务名存在，不能重复！");

            if (!string.IsNullOrEmpty(data.Cron) && !CronExpression.IsValidExpression(data.Cron))
                throw new ParamiterException($"cron表达式不正确");

            if (string.IsNullOrEmpty(data.RequestUrl) && data.RunType != RequestTypeEnum.Run)
                throw new ParamiterException($"地址不能为空");

            if (data.RequestUrl.NotNullOrWhiteSpace() && data.RunType != RequestTypeEnum.Run)
            {
                data.AssemblyName = "RuoVea.Service";
                data.ClassName = "Job.JobHttpRequest";
            }
            if (data.RunType == RequestTypeEnum.Run && (data.AssemblyName.IsEmpty() || data.ClassName.IsEmpty()))
                throw new ParamiterException($"程序集或者类名不能为空");

            //从 Dto 映射到 实体
            var model = _taskDetailService.InDtoToDetail(data);

            model.CreateTime = DateTime.Now;
            model.Id = IdGenerator.Id;
            model.Deleted = ExEnum.IsDelete.N;
            model.Status = ExEnum.StatusEnum.DISABLE;
            //if (data.RunTimes == null) model.RunTimes = 0;

            var taskResult = await _taskDetailService.InsertAsync(model);

            return taskResult;
        }
        #endregion

        #region 更新任务
        /// <summary>
        /// 更新任务
        /// </summary>
        /// <returns></returns>
        private async Task<dynamic> Update(TaskDetailInDto data)
        {
            //判断是否已经存在
            if (await _taskDetailService.AnyAsync(m => m.JobName == data.JobName && m.Id != data.Id))
                throw new ParamiterException($"更新 {data.JobName} 失败，该用任务存在，不能重复！");
            if (string.IsNullOrEmpty(data.Cron) && data.TriggerType == TriggerTypeEnum.Corn)
                throw new ParamiterException($"触发器 Corn 模式下，运行时间表达式必须填写");
            if (!string.IsNullOrEmpty(data.Cron) && !CronExpression.IsValidExpression(data.Cron))
                throw new ParamiterException($"cron表达式不正确");

            var tasksQz = await _taskDetailService.GetByIdAsync(data.Id);

            if (string.IsNullOrEmpty(data.RequestUrl) && data.RunType != RequestTypeEnum.Run)
                throw new ParamiterException($"api地址不能为空");

            if (data.RequestUrl.NotNullOrWhiteSpace() && data.RunType != RequestTypeEnum.Run)
            {
                data.AssemblyName = "RuoVea.QuartzNetUI";
                data.ClassName = "Job.HttpJob";
            }
            if (tasksQz.Data.Status == ExEnum.StatusEnum.ENABLE)
                throw new ParamiterException($"该任务正在运行中，请先停止在更新");

            var model = _taskDetailService.InDtoToDetail(data);

            RestfulResult restfulResult = await _taskDetailService.UpdateAsync(model);
            if (restfulResult.Code == CodeStatus.OK)
            {
                var respon = await _taskSchedulerServer.UpdateTaskScheduleAsync(new TaskDetailInDto { Id = data.Id, JobName = tasksQz.Data.JobName, JobGroup = tasksQz.Data.JobGroup });
                return respon;
            }
            return restfulResult;
        }
        #endregion

        #region 删除任务
        /// <summary>
        /// 删除任务
        /// </summary>
        /// <param name="Id">任务主键</param>
        /// <returns></returns>
        /// <exception cref="ParamiterException"></exception>
        [HttpGet("Remove/{Id:long}")]
        public async Task<dynamic> Remove(long Id)
        {
            if (Id == 0)
                throw new ParamiterException("删除任务 Id 不能为空");
            if (!await _taskDetailService.AnyAsync(x => x.Id == Id))
                throw new ParamiterException("任务不存在");

            var tasksQz = await _taskDetailService.GetByIdAsync(Id);
            if (tasksQz.Code == ExEnum.CodeStatus.OK)
            {
                var taskResult = await _taskSchedulerServer.DeleteTaskScheduleAsync(
                    new TaskDetailInDto { Id = tasksQz.Data.Id, JobGroup = tasksQz.Data.JobGroup, JobName = tasksQz.Data.JobName });
                await _taskDetailService.DeleteAsync(new TaskDetaiDeleteDto { Id = Id });
                return taskResult;
            }
            return tasksQz;
        }
        #endregion

        #region 启动任务
        /// <summary>
        /// 启动任务
        /// </summary>
        /// <param name="Id">任务主键</param>
        /// <returns></returns>
        [HttpGet("start/{Id:long}")]
        public async Task<dynamic> Start(long Id)
        {
            if (Id == 0)
                throw new ParamiterException("启动任务 Id 不能为空");

            if (!await _taskDetailService.AnyAsync(x => x.Id == Id))
                throw new ParamiterException("任务不存在");

            var tasksQz = await _taskDetailService.GetByIdAsync(Id);
            if (tasksQz.Code == ExEnum.CodeStatus.OK)
            {
                var model = _taskDetailService.DetailToInDto(tasksQz.Data);
                var taskResult = await _taskSchedulerServer.AddTaskScheduleAsync(model);
                if (taskResult.Code == CodeStatus.OK)
                {
                    tasksQz.Data.Status = ExEnum.StatusEnum.ENABLE;
                    await _taskDetailService.UpdateAsync(tasksQz.Data);
                }
                return taskResult;
            }
            return tasksQz;
        }
        #endregion

        #region 停止任务
        /// <summary>
        /// 停止任务
        /// </summary>
        /// <param name="Id">任务主键</param>
        /// <returns></returns>
        [HttpGet("stop/{Id:long}")]
        public async Task<dynamic> Stop(long Id)
        {
            if (0 == Id)
                throw new ParamiterException("停止任务 Id 不能为空");

            if (!await _taskDetailService.AnyAsync(x => x.Id == Id))
                throw new ParamiterException("任务不存在");

            var tasksQz = await _taskDetailService.GetByIdAsync(Id);
            if (tasksQz.Code == ExEnum.CodeStatus.OK)
            {
                var taskResult = await _taskSchedulerServer.DeleteTaskScheduleAsync(
                    new TaskDetailInDto
                    {
                        Id = tasksQz.Data.Id,
                        JobGroup = tasksQz.Data.JobGroup,
                        JobName = tasksQz.Data.JobName
                    });
                //await _taskSchedulerServer.PauseTaskScheduleAsync(tasksQz);
                tasksQz.Data.Status = ExEnum.StatusEnum.DISABLE;
                await _taskDetailService.UpdateAsync(tasksQz.Data);
                taskResult.Message = "暂停任务成功.";
                return taskResult;
            }
            return tasksQz;
        }
        #endregion

        #region 定时任务立即执行一次
        /// <summary>
        /// 定时任务立即执行一次
        /// </summary>
        /// <param name="Id">任务主键</param>
        /// <returns></returns>
        [HttpGet("run/{Id:long}")]
        public async Task<dynamic> Run(long Id)
        {
            if (!await _taskDetailService.AnyAsync(x => x.Id == Id))
                throw new ParamiterException("任务不存在");

            var tasksQz = await _taskDetailService.GetByIdAsync(Id);
            if (tasksQz.Code == ExEnum.CodeStatus.OK)
            {
                var model = _taskDetailService.DetailToInDto(tasksQz.Data);
                var taskResult = await _taskSchedulerServer.RunTaskScheduleAsync(model);
                if (taskResult.Code == CodeStatus.OK)
                {
                    await _taskSchedulerServer.DeleteTaskScheduleAsync(
                        new TaskDetailInDto { Id = tasksQz.Data.Id, JobGroup = tasksQz.Data.JobGroup, JobName = tasksQz.Data.JobName });
                }
                return taskResult;
            }
            return tasksQz;
        }

        #endregion
    }
}